Add a flag to force resolution of a fingerprint
authorAlex Crichton <alex@alexcrichton.com>
Sun, 1 Feb 2015 23:21:15 +0000 (15:21 -0800)
committerAlex Crichton <alex@alexcrichton.com>
Wed, 4 Feb 2015 22:59:53 +0000 (14:59 -0800)
Previously if a fingerprint was considered fresh based on mtime, it would not
get updated once a compilation finished with the new precise fingerprint (it
would use the older fingerprint). This alters the `resolve` method to take a
flag which disables this behavior and forces looking at the filesystem for a
fingerprint.

Closes #1259

src/cargo/ops/cargo_rustc/fingerprint.rs
tests/test_cargo_compile_path_deps.rs
tests/test_cargo_freshness.rs

index 297173a31a3f9e48f2b55af1f48a1abcbefbb87d..f2adf8815987d6eccfa6b39f6a1e0501c402fe21 100644 (file)
@@ -103,22 +103,25 @@ pub fn prepare_target<'a, 'b>(cx: &mut Context<'a, 'b>,
 pub struct Fingerprint {
     extra: String,
     deps: Vec<Fingerprint>,
-    personal: Personal,
+    local: LocalFingerprint,
 }
 
 #[derive(Clone)]
-enum Personal {
-    Known(String),
-    Unknown(Path),
+enum LocalFingerprint {
+    Precalculated(String),
+    MtimeBased(Option<u64>, Path),
 }
 
 impl Fingerprint {
-    fn resolve(&self) -> CargoResult<String> {
-        let mut deps: Vec<_> = try!(self.deps.iter().map(|s| s.resolve()).collect());
+    fn resolve(&self, force: bool) -> CargoResult<String> {
+        let mut deps: Vec<_> = try!(self.deps.iter().map(|s| {
+            s.resolve(force)
+        }).collect());
         deps.sort();
-        let known = match self.personal {
-            Personal::Known(ref s) => s.clone(),
-            Personal::Unknown(ref p) => {
+        let known = match self.local {
+            LocalFingerprint::Precalculated(ref s) => s.clone(),
+            LocalFingerprint::MtimeBased(Some(n), _) if !force => n.to_string(),
+            LocalFingerprint::MtimeBased(_, ref p) => {
                 debug!("resolving: {}", p.display());
                 try!(fs::stat(p)).modified.to_string()
             }
@@ -173,26 +176,24 @@ fn calculate<'a, 'b>(cx: &mut Context<'a, 'b>,
         calculate(cx, p, t, kind)
     }).collect::<CargoResult<Vec<_>>>());
 
-    // And finally, calculate what our own personal fingerprint is
-    let personal = if use_dep_info(pkg, target) {
+    // And finally, calculate what our own local fingerprint is
+    let local = if use_dep_info(pkg, target) {
         let dep_info = dep_info_loc(cx, pkg, target, kind);
-        match try!(calculate_target_mtime(&dep_info)) {
-            Some(i) => Personal::Known(i.to_string()),
-            None => {
-                // If the dep-info file does exist (but some other sources are
-                // newer than it), make sure to delete it so we don't pick up
-                // the old copy in resolve()
-                let _ = fs::unlink(&dep_info);
-                Personal::Unknown(dep_info)
-            }
+        let mtime = try!(calculate_target_mtime(&dep_info));
+
+        // if the mtime listed is not fresh, then remove the `dep_info` file to
+        // ensure that future calls to `resolve()` won't work.
+        if mtime.is_none() {
+            let _ = fs::unlink(&dep_info);
         }
+        LocalFingerprint::MtimeBased(mtime, dep_info)
     } else {
-        Personal::Known(try!(calculate_pkg_fingerprint(cx, pkg)))
+        LocalFingerprint::Precalculated(try!(calculate_pkg_fingerprint(cx, pkg)))
     };
     let fingerprint = Fingerprint {
         extra: extra,
         deps: deps,
-        personal: personal,
+        local: local,
     };
     cx.fingerprints.insert(key, fingerprint.clone());
     Ok(fingerprint)
@@ -244,7 +245,7 @@ pub fn prepare_build_cmd(cx: &mut Context, pkg: &Package, kind: Kind,
     let new_fingerprint = Fingerprint {
         extra: String::new(),
         deps: Vec::new(),
-        personal: Personal::Known(new_fingerprint),
+        local: LocalFingerprint::Precalculated(new_fingerprint),
     };
 
     let is_fresh = try!(is_fresh(&loc, &new_fingerprint));
@@ -287,7 +288,7 @@ pub fn prepare_init(cx: &mut Context, pkg: &Package, kind: Kind)
 fn prepare(is_fresh: bool, loc: Path, fingerprint: Fingerprint) -> Preparation {
     let write_fingerprint = Work::new(move |_| {
         debug!("write fingerprint: {}", loc.display());
-        let fingerprint = try!(fingerprint.resolve().chain_error(|| {
+        let fingerprint = try!(fingerprint.resolve(true).chain_error(|| {
             internal("failed to resolve a pending fingerprint")
         }));
         try!(File::create(&loc).write_str(fingerprint.as_slice()));
@@ -317,7 +318,7 @@ fn is_fresh(loc: &Path, new_fingerprint: &Fingerprint) -> CargoResult<bool> {
     };
 
     let old_fingerprint = try!(file.read_to_string());
-    let new_fingerprint = match new_fingerprint.resolve() {
+    let new_fingerprint = match new_fingerprint.resolve(false) {
         Ok(s) => s,
         Err(..) => return Ok(false),
     };
index 31d3ad185537b76d3cf73dac5651fd65edd8e988..9286d7ed20e73329bfccf05ec7c63fbd89bec741 100644 (file)
@@ -1,4 +1,5 @@
-use std::old_io::{fs, File, USER_RWX};
+use std::old_io::{fs, File, USER_RWX, timer};
+use std::time::Duration;
 
 use support::{project, execs, main_file, cargo_dir};
 use support::{COMPILING, RUNNING};
@@ -353,7 +354,7 @@ test!(deep_dependencies_trigger_rebuild {
     //
     // We base recompilation off mtime, so sleep for at least a second to ensure
     // that this write will change the mtime.
-    p.root().move_into_the_past().unwrap();
+    timer::sleep(Duration::seconds(1));
     File::create(&p.root().join("baz/src/baz.rs")).write_str(r#"
         pub fn baz() { println!("hello!"); }
     "#).unwrap();
@@ -366,7 +367,7 @@ test!(deep_dependencies_trigger_rebuild {
                                             COMPILING, p.url())));
 
     // Make sure an update to bar doesn't trigger baz
-    p.root().move_into_the_past().unwrap();
+    timer::sleep(Duration::seconds(1));
     File::create(&p.root().join("bar/src/bar.rs")).write_str(r#"
         extern crate baz;
         pub fn bar() { println!("hello!"); baz::baz(); }
index 407a04a94ae6d467990d040751c3843ee6de94fd..a4d302a862439fa18a5925f178b0dc37f162c6af 100644 (file)
@@ -135,3 +135,40 @@ test!(rebuild_sub_package_then_while_package {
     assert_that(p.process(cargo_dir().join("cargo")).arg("build"),
                 execs().with_status(0));
 });
+
+test!(changing_features_is_ok {
+    let p = project("foo")
+        .file("Cargo.toml", r#"
+            [package]
+            name = "foo"
+            authors = []
+            version = "0.0.1"
+
+            [features]
+            foo = []
+        "#)
+        .file("src/lib.rs", "");
+
+    assert_that(p.cargo_process("build"),
+                execs().with_status(0)
+                       .with_stdout("\
+[..]Compiling foo v0.0.1 ([..])
+"));
+
+    assert_that(p.process(cargo_dir().join("cargo")).arg("build")
+                 .arg("--features").arg("foo"),
+                execs().with_status(0)
+                       .with_stdout("\
+[..]Compiling foo v0.0.1 ([..])
+"));
+
+    assert_that(p.process(cargo_dir().join("cargo")).arg("build"),
+                execs().with_status(0)
+                       .with_stdout("\
+[..]Compiling foo v0.0.1 ([..])
+"));
+
+    assert_that(p.process(cargo_dir().join("cargo")).arg("build"),
+                execs().with_status(0)
+                       .with_stdout(""));
+});